package com.alawn.security.config;

import com.alawn.framework.core.utils.Digests;
import com.alawn.framework.core.web.faces.cache.SpringCacheManagerWrapper;
import com.alawn.security.config.credentials.RetryLimitHashedCredentialsMatcher;
import com.alawn.security.config.filter.CaptchaValidateFilter;
import com.alawn.security.config.filter.KickoutSessionControlFilter;
import com.alawn.security.config.filter.SecurityFormAuthenticationFilter;
import com.alawn.security.config.realm.ShiroRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

	private static final int KEEP_DAYS = 30;

	@Value("${myzone.faces.web.loginTimes}")
	private int loginTimes;

	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
		// 必须配置 SecurityManager
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		// filters配置
		Map<String, Filter> filters = new LinkedHashMap<>();
		filters.put("kickout", kickoutSessionControlFilter());
		filters.put("authc", new SecurityFormAuthenticationFilter());
		filters.put("captcha", new CaptchaValidateFilter());

		shiroFilterFactoryBean.setFilters(filters);
		// 拦截配置
		Map<String, String> filterChainDefinitions = new LinkedHashMap<>();
		filterChainDefinitions.put("/javax.faces.resource/**", "anon");

		filterChainDefinitions.put("/anon/**", "anon");
		filterChainDefinitions.put("/security/**", "anon");
		filterChainDefinitions.put("/login", "authc,captcha,kickout");
		filterChainDefinitions.put("/index", "authc");
		filterChainDefinitions.put("/logout", "logout");

		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitions);

		shiroFilterFactoryBean.setLoginUrl("/login");
		shiroFilterFactoryBean.setSuccessUrl("/index");
		shiroFilterFactoryBean.setUnauthorizedUrl("/access");
		return shiroFilterFactoryBean;
	}

	// 实现动态url拦截
	@Bean
	public DefaultFilterChainManager defaultFilterChainManager(AbstractShiroFilter shiroFilter) {
		PathMatchingFilterChainResolver resolver = (PathMatchingFilterChainResolver) shiroFilter
				.getFilterChainResolver();
		return (DefaultFilterChainManager) resolver.getFilterChainManager();
	}

	@Bean
	public AbstractShiroFilter shiroFilter(ShiroFilterFactoryBean filterFactoryBean) {
		try {
			return (AbstractShiroFilter) filterFactoryBean.getObject();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	// 安全管理器
	@Bean
	public SecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		// 注入自定义的Realm
		securityManager.setRealm(shiroRealm());
		// 注入缓存处理器
		securityManager.setCacheManager(ehCacheManager());
		// session管理
		securityManager.setSessionManager(sessionManager());
		// 注入记住账号处理器
		securityManager.setRememberMeManager(rememberMeManager());
		return securityManager;
	}

	// Realm实现
	@Bean
	public ShiroRealm shiroRealm() {
		ShiroRealm shiroRealm = new ShiroRealm();
		shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return shiroRealm;
	}

	// 凭证匹配器
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher matcher = new RetryLimitHashedCredentialsMatcher(ehCacheManager(), loginTimes);
		matcher.setHashAlgorithmName(Digests.SHA1);
		matcher.setHashIterations(Digests.HASH_INTERATIONS);
		matcher.setStoredCredentialsHexEncoded(true);

		return matcher;
	}

	// 登入人数控制
	@Bean
	public KickoutSessionControlFilter kickoutSessionControlFilter() {
		KickoutSessionControlFilter filter = new KickoutSessionControlFilter();
		filter.setCacheManager(ehCacheManager());
		filter.setSessionManager(sessionManager());
		filter.setKickoutAfter(false);
		filter.setMaxSession(1);
		filter.setKickoutUrl("/login?kickout=1");
		return filter;
	}

	@Bean
	@ConditionalOnMissingBean
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
		return defaultAdvisorAutoProxyCreator;
	}

	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	// 缓存管理器 使用Ehcache实现
	@Bean
	public CacheManager ehCacheManager() {
		SpringCacheManagerWrapper ehCacheManager = new SpringCacheManagerWrapper();
		ehCacheManager.setCacheManager(springCacheManager());
		return ehCacheManager;
	}

	@Bean
	public EhCacheCacheManager springCacheManager() {
		return new EhCacheCacheManager(ehCacheManagerFactoryBean().getObject());
	}

	@Bean
	public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
		EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
		cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache/ehcache.xml"));
		cacheManagerFactoryBean.setShared(true);
		return cacheManagerFactoryBean;
	}

	@Bean
	public CookieRememberMeManager rememberMeManager() {
		CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
		SimpleCookie cookie = new SimpleCookie("rememberMe");
		cookie.setHttpOnly(true);
		cookie.setMaxAge(60 * 60 * 24 * KEEP_DAYS);
		rememberMeManager.setCookie(cookie);
		return rememberMeManager;
	}

	@Bean
	public DefaultWebSessionManager sessionManager() {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		sessionManager.setCacheManager(ehCacheManager());
		return sessionManager;
	}
}
